package timetable.io;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
import java.util.Vector;

import timetable.exceptions.InvalidEntryException;
import timetable.exceptions.TimeTableParserErrors;
import timetable.exceptions.ToleranceReachedException;
import timetable.interfaces.ProgressObservable;
import timetable.interfaces.filefilter.SupportedFileFilter;
import timetable.tools.StringTools;

/**
 * TimeTableDataFile is a structured way of representing a file containing Time Table entries. Structured means
 * organized in data-structures from type TimeTableEntry
 * 
 * @author Sander Eskens
 * @see	TimeTableEntry
 *
 */

public class TimeTableDataFile extends ProgressObservable
{

	//private static final String[] filestructure = { "id", "duur", "unknown", "docent", "cursus_id", "cursus_naam", "klas", "lokaal", "unknown", "unknown", "unknown", "unknown", "dag", "aanvang"};
	// 0		1		2			3			4			5			6		7			8			9		10		11			12		13
	
	private static final String DEFAULT_DELIMITER = "\t";
	private static final String DEFAULT_SUBDELIMITER = ";";
	
	private String delimiter;
	private String filepath;
	private String subdelimiter;
	
	private Vector<TimeTableEntry> entries;
	
	private Iterator<TimeTableEntry> current;
	
	/**
	 * Extended Constructor for a TimeTableData object. Specifying filepath, delimiter and subdelimiter
	 *
	 * @param	filepath		specifies the path to the file contianing the raw timetable information
	 * 			delimiter		delimiter for the raw data. When 'null' is given for this parameter the default delimiter '\t' is used 
	 * 			subdelimiter	subdelimiter for the raw data. When 'null' is given for this parameter the default subdelimiter ';' is used
	 */
	public TimeTableDataFile(String filepath, String delimiter, String subdelimiter)
	{
		super();
		entries = new Vector<TimeTableEntry>();
		
		this.filepath = filepath;
		
		if (delimiter == null)
			delimiter = DEFAULT_DELIMITER;
		if (subdelimiter == null) 
			subdelimiter = DEFAULT_SUBDELIMITER;
		
		this.delimiter = delimiter;
		this.subdelimiter = subdelimiter;
		
		current = entries.iterator();
	}
	
	/**
	 * Basic Constructor for a TimeTableData object. Specifying onle the filepath. The default delimiter and subdelimiter is used.
	 *
	 * @param	filepath		specifies the path to the file contianing the raw timetable information.
	 */
	public TimeTableDataFile(String filepath)
	{
		this(filepath, null, null);
	}
	
	/**
	 * Set the delimiter to the specified value
	 *
	 * @param	delimiter		the new to use delimiter.
	 */
	public void setDelimiter(String delimiter)
	{
		this.delimiter = delimiter;
	}
	
	/**
	 * Set the subdelimiter to the specified value
	 *
	 * @param	subdelimiter	the new to use subdelimiter.
	 */
	public void setSubDelimiter(String subdelimiter)
	{
		this.subdelimiter = subdelimiter;
	}
	
	/**
	 * This method checks whether the file for the given filepath exists. 
	 *
	 * @return	boolean	the result of whether the file exists or not.
	 */
	public boolean fileExists()
	{
		return new File(this.filepath).exists();
	}
	
	/**
	 * This sets the iterator to the beginning of the entrielist. You need to call this method everytime you want to iterate over the entries.
	 */
	public void reset()
	{
		current = entries.iterator();
	}
	
	/**
	 * Returns whether there is a next entry in the file or not.
	 * 
	 * @return	boolean	returns true when there is a next entry.
	 * 					returns false when there the end of the file has been reached.
	 */
	public boolean hasNext()
	{
		return current.hasNext();
	}
	
	/**
	 * Returns the next entry of the TimeTableDataFile
	 * 
	 * @return TimeTableEntry	datatype containing all fields of a timetable-entry.
	 */
	public TimeTableEntry next()
	{
		return current.next();
	}
	
	/**
	 * Gets the total number of entries.
	 * 
	 * @return	int	the amount of entries in the timetable-file.
	 */
	public int getEntryCount()
	{
		return entries.size();
	}
	
	/**
	 * Starts to parse the file the info is given for. When an error is occurred the matching entry
	 * will be set invalid.
	 * 
	 * @param	tolerance				The number of errors that will be toleranced. When the number is reached
	 * 									a ToleranceReachedException will be thrown and the parsing-procedure will
	 * 									be aborted.
	 * 
	 * @return	TimeTableParserErrors	Will be null if no errors are found. Else is contains a list of errors
	 * 
	 * @throws	IOException				this exception will be thrown when there is a problem regarding file-IO.
	 * @throws ToleranceReachedException 
	 */
	public TimeTableParserErrors parse(int tolerance) throws IOException, ToleranceReachedException
	{
		File file = new File(filepath);
		if (!file.exists()) throw new IOException("File '" + StringTools.getFileName(filepath) + "' doesn't exist");
		if (!file.canRead()) throw new IOException("Unable to read '" + StringTools.getFileName(filepath) + "'");
		
		if (!new SupportedFileFilter().accept(file)) throw new IOException("Unsupported extension of file '" + StringTools.getFileName(filepath) + "'");
		
		BufferedReader fReader = new BufferedReader(new FileReader(file));
		String line = fReader.readLine();
		TimeTableParserErrors parserErrors = new TimeTableParserErrors("Errors found while parsing '" + StringTools.getFileName(filepath) + "'"); 
		
		long lineNr = 1;
		long fSize = file.length();
		long readSize = 0;
		
		//System.out.println(fSize);
		//System.out.println(readSize);
		
		while (line != null)
		{
			readSize += line.length() + 1;
			notifyObservers(new Integer((int) (((double)readSize / (double) fSize)*100) ));
			int column_id = 0;
			TimeTableEntry entry = new TimeTableEntry((int)lineNr);
			String[] data = line.split(delimiter);
			for (String d : data)
			{
				d = d.trim();
				switch (column_id) 
				{
				case 1:
					entry.setDuur(d);
					break;
				case 2:
					break;
				case 3:
					entry.setDocent(d);
					break;
				case 4:
					break;
				case 5:
					entry.setCursusnaam(d);
					break;
				case 6:
					entry.setKlas(d);
					break;
				case 7:
					entry.setLokaal(d);
					break;
				case 12:
					entry.setDag(d);
					break;
				case 13:
					entry.setAanvang(d);
					break;
				default:
					break;
				}
				column_id++;
			}
			try {
				entry.check();
			} catch (InvalidEntryException e) {
				parserErrors.getList().add(e);
				if (parserErrors.getList().size() >= tolerance)
					throw new ToleranceReachedException("The tolerance is reached. Parsing aborted");
			}
			entries.add(entry);
			//System.out.println(entry.getId());
			line = fReader.readLine();
			lineNr++;
		}
		notifyObservers(new Integer(100));
		
		if (parserErrors.getList().size() != 0) return parserErrors;
		return null;
	}
	
	/**
	 * Get a metadata string representation.
	 * 
	 * @return	String	string containing the metadata
	 */
	@Override
	public String toString()
	{
		return 	"TimeTableDataFile\n" +
				"=================\n" +
				"file:\t" + this.filepath + "\n" +
				"delimiter:\t" + this.delimiter + "\n" +
				"subdelimiter:\t" + this.subdelimiter + "\n" +
				"entry count:\t" + this.getEntryCount();
	}
}
